/*
 * Pane.cpp
 *
 *  Created on: Apr 13, 2009
 *      Author: wrice
 */

#include <sstream>
#include "PANEImp.h"
namespace PANEImp {

string PANEElement::AttrNameWidth = "WIDTH";
string PANEElement::AttrNameHeight = "HEIGHT";
string PANEElement::AttrNameVisible = "VISIBLE";
string PANEElement::AttrNameForegroundColor = "FGCOLOR";
string PANEElement::AttrNameBackgroundColor = "BGCOLOR";
string PANEElement::AttrNameFontName = "FONT_NAME";
string PANEElement::AttrNameFontHeight = "FONT_HEIGHT";
string PANEElement::AttrValueVisibleTrue = "TRUE";
string PANEElement::AttrValueVisibleFalse = "FALSE";

PANEElement::PANEElement(DOMString nodeName, PANEDocument * ownerDocument)
: ElementImp(nodeName, ownerDocument)
, repaintRequested_(true)
{
}

long PANEElement::getTopOffset() {
	PANEElement * const parent = dynamic_cast< PANEElement * >( getParentNode() );
	if( NULL == parent ) return 0;

	PANEElement * const prevSibling = dynamic_cast< PANEElement * >( getPreviousSibling() );
	if( NULL == prevSibling )
		return parent->getTopOffset();

	unsigned long availableWidth = getAvailableWidth();
	if( availableWidth < getActualWidth( availableWidth ) ) {
		long lowestBottomOffset = prevSibling->getBottomOffset();
		for( unsigned long idx = 0; idx < NodeImp::getMyIndexOnParent( this ); ++idx ) {
			PANEElement * sibling = dynamic_cast< PANEElement * >( parent->getChildNodes()->item(idx) );
			lowestBottomOffset = std::max(lowestBottomOffset, sibling->getBottomOffset() );
		}
		return lowestBottomOffset;
	}

	return prevSibling->getTopOffset();
}
long PANEElement::getBottomOffset(){
	return getTopOffset() + getActualHeight( getAvailableWidth() );
}
long PANEElement::getLeftOffset(){
	PANEElement * const parent = dynamic_cast< PANEElement * >( getParentNode() );
	if( NULL == parent ) return 0;

	PANEElement * const prevSibling = dynamic_cast< PANEElement * >( getPreviousSibling() );
	if( NULL == prevSibling )
		return parent->getLeftOffset();

	const unsigned long availableWidth = getAvailableWidth();
	if( availableWidth  < getActualWidth( availableWidth ) )
		return parent->getLeftOffset();

	return prevSibling->getRightOffset();
}
long PANEElement::getRightOffset(){
	return getLeftOffset() + getActualWidth( getAvailableWidth() );
}

long PANEElement::getAvailableWidth() {
	PANEElement * const parent = dynamic_cast< PANEElement * >( getParentNode() );
	if( NULL == parent ) return getWidth();
	const long parentAvailableWidth = parent->getAvailableWidth();

	PANEElement * const prevSibling = dynamic_cast< PANEElement * >( getPreviousSibling() );
	if( NULL == prevSibling )
		return parentAvailableWidth;

	PANEElement * const firstSibling = dynamic_cast< PANEElement * >( parent->getFirstChild() );
	const long firstLeft = firstSibling->getLeftOffset();
	const long prevRight = prevSibling->getRightOffset();
	const long availableWidth = parentAvailableWidth - (prevRight -  firstLeft);

	if( hasDesiredWidth() )
		return std::min( (long) getWidth(), availableWidth );
	return availableWidth;
}

unsigned long PANEElement::getActualWidth( unsigned long availableWidth ) {
	const WidthHeight widthHeight = getActualWidthHeight( availableWidth );
	return Field< 0 >( widthHeight );
}

unsigned long PANEElement::getActualHeight( unsigned long availableWidth ) {
	const WidthHeight widthHeight = getActualWidthHeight( availableWidth );
	return Field< 1 >( widthHeight );
}

PANEElement::WidthHeight PANEElement::getActualWidthHeight( unsigned long availableWidth ) {
	unsigned long maxWidthFromChild = 0;
	unsigned long widthForEachLine = 0;
	unsigned long totalHeight = 0;
	unsigned long maxHeightForEachLine = 0;
	for( unsigned int i = 0; i < this->getChildNodes()->getLength(); ++i ) {
		PANEElement * child = dynamic_cast< PANEElement * >( this->getChildNodes()->item(i) );
		if( NULL == child ) continue;

		const unsigned long widthOfEachChild = (unsigned long) child->getActualWidth( availableWidth );
		const unsigned long heightOfEachChild = (unsigned long) child->getActualHeight( availableWidth );

		if( widthForEachLine + widthOfEachChild < availableWidth ) {
			widthForEachLine += widthOfEachChild;
			maxHeightForEachLine = std::max( maxHeightForEachLine, heightOfEachChild );
		} else {
			maxWidthFromChild = std::max( maxWidthFromChild, widthForEachLine );
			widthForEachLine = widthOfEachChild;
			totalHeight += maxHeightForEachLine;
			maxHeightForEachLine = heightOfEachChild;
		}
	}

	maxWidthFromChild = std::max( maxWidthFromChild, widthForEachLine );
	totalHeight += maxHeightForEachLine;

	const unsigned long actualWidth = std::max( maxWidthFromChild, getWidth() );
	const unsigned long actualHeight = std::max( totalHeight, getHeight() );
	WidthHeight widthHeight;
	Field< 0 >( widthHeight ) = actualWidth;
	Field< 1 >( widthHeight ) = actualHeight;
	return widthHeight;
}

bool PANEElement::getVisible(){
	PANEElement * const parent = dynamic_cast< PANEElement * >( getParentNode() );
	if( NULL == parent ) return getAttribute(AttrNameVisible) != AttrValueVisibleFalse;
	if( false == parent->getVisible() ) return false;
	return getAttribute(AttrNameVisible) != AttrValueVisibleFalse;
}
unsigned long PANEElement::getForegroundColor(){
	return 0;
}
unsigned long PANEElement::getBackgroundColor(){
	return 0;
}
DOMString PANEElement::getFontName(){
	return getAttribute(AttrNameFontName);
}
DOMString PANEElement::getFontHeight(){
	return getAttribute(AttrNameFontHeight);
}

bool PANEElement::hasDesiredWidth() {
	return hasAttribute( AttrNameWidth );
}
bool PANEElement::hasDesiredHeight() {
	return hasAttribute( AttrNameHeight );
}
unsigned long PANEElement::getWidth() {
	unsigned long widthFromAttr = 0;
	if( hasAttribute(AttrNameWidth) ) {
		istringstream iss( getAttribute(AttrNameWidth) );
		iss >> widthFromAttr;
	}
	return widthFromAttr;
}

unsigned long PANEElement::getHeight() {
	unsigned long heightFromAttr = 0;
	if( hasAttribute(AttrNameHeight) ) {
		istringstream iss( getAttribute(AttrNameHeight) );
		iss >> heightFromAttr;
	}
	return heightFromAttr;
}

void PANEElement::setWidth(unsigned long width) {
	ostringstream oss;
	oss << width;
	setAttribute( AttrNameWidth, oss.str() );
}
void PANEElement::setHeight(unsigned long height) {
	ostringstream oss;
	oss << height;
	setAttribute( AttrNameHeight, oss.str() );
}

void PANEElement::setVisible( bool visible ) {
	setAttribute(AttrNameVisible, visible ? AttrValueVisibleTrue : AttrValueVisibleFalse );
}

void PANEElement::setFontName( DOMString fontName ) {
	setAttribute( AttrNameFontName, fontName );
}

void PANEElement::setFontHeight( DOMString fontHeight ) {
	setAttribute( AttrNameFontHeight, fontHeight );
}

void PANEElement::requestRepaint() {
	repaintRequested_ = true;
}

bool PANEElement::isRepaintRequested() {
	return repaintRequested_;
}

void PANEElement::repainted() {
	repaintRequested_ = false;
}


}
